﻿using System;
using System.ComponentModel;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Linq;
using UtilZ.Dotnet.Ex.Base;

namespace UtilZ.Dotnet.Ex.Model
{
    /// <summary>
    /// 通信传输数据基类
    /// </summary>
    public abstract class TransferDataAbs : ITransferData
    {
        /// <summary>
        /// 数据Tag
        /// </summary>
        [Browsable(false)]
        public object Tag { get; set; }

        /// <summary>
        /// 
        /// </summary>
        protected DataParseResult _parseResult = DataParseResult.Succes;
        /// <summary>
        /// 数据解析结果
        /// </summary>
        [Browsable(false)]
        public DataParseResult ParseResult
        {
            get { return this._parseResult; }
        }

        /// <summary>
        /// 设置数据解析结果
        /// </summary>
        public void SetParseResult(DataParseResult parseResult)
        {
            this._parseResult = parseResult;
        }

        /// <summary>
        /// 指示当前数据在发送拥堵时是否允许丢弃[true:允许丢弃;fasle:不允许丢弃]
        /// </summary>
        private bool _allowDiscard = true;
        /// <summary>
        /// 指示当前数据在发送拥堵时是否允许丢弃[true:允许丢弃;fasle:不允许丢弃]
        /// </summary>
        public bool AllowDiscard
        {
            get { return this._allowDiscard; }
            set { this._allowDiscard = value; }
        }

        /// <summary>
        /// 设置当前数据在发送拥堵时是否允许丢弃
        /// </summary>
        /// <param name="allowDiscard">[true:允许丢弃;fasle:不允许丢弃]</param>
        protected void SetAllowDiscard(bool allowDiscard)
        {
            this._allowDiscard = allowDiscard;
        }



        /// <summary>
        /// 构造函数
        /// </summary>
        public TransferDataAbs()
        {

        }






        /// <summary>
        /// 获取当前对象的数据buffer.
        /// 注:
        /// 当数据buffer中的数据类型(BufferData.DataType)为BufferDataType.File时,外数据使用完成后,需要外部删除文件;
        /// 当数据buffer中的数据类型(BufferData.DataType)为BufferDataType.Stream时,外数据使用完成后,需要外部对bufferData.Stream调用Dispose方法.
        /// </summary>
        /// <param name="options">选项,为null使用默认选项</param>
        /// <returns>数据buffer</returns>
        public virtual DataSource ToBufferData(TransferDataOptions options = null)
        {
            if (options == null)
            {
                options = TransferDataOptions.Default;
            }

            DataSource bufferData;
            long length = this.GetWriteDataLength();
            if (length < 0)
            {
                using (var ms = new MemoryStream())
                {
                    this.WriteData(null, new BinaryWriter(ms));
                    ms.Flush();
                    bufferData = new DataSource(ms.ToArray());
                }
            }
            else if (length <= options.Limit)
            {
                byte[] buffer = new byte[length];
                bufferData = new DataSource(buffer);
                using (var ms = new MemoryStream(buffer))
                {
                    this.WriteData(bufferData, new BinaryWriter(ms));
                }
            }
            else
            {
                if (options.LimitType == TransferDataOptionsLimitDataType.File)
                {
                    string filePath = options.GenerateBufferDataFilePath();
                    bufferData = new DataSource(filePath, 0, length);
                    try
                    {
                        var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None);
                        bufferData.AttatchDisposabler(fs);
                        this.WriteData(bufferData, new BinaryWriter(fs));
                        fs.Flush();
                    }
                    catch (Exception)
                    {
                        bufferData.Dispose();
                        FileEx.TryDeleFile(filePath);
                        throw;
                    }
                }
                else
                {
                    string mapName = Guid.NewGuid().ToString();
                    var disposabler = new Disposabler();
                    var mmf = MemoryMappedFile.CreateNew(mapName, length, MemoryMappedFileAccess.ReadWrite);
                    disposabler.Add(mmf, 0);
                    MemoryMappedViewStream mms = mmf.CreateViewStream();
                    disposabler.Add(mms, 1);
                    bufferData = new DataSource(mms, 0, length);
                    bufferData.AttatchDisposabler(disposabler);

                    try
                    {
                        this.WriteData(bufferData, new BinaryWriter(mms));
                        mms.Flush();
                    }
                    catch (Exception)
                    {
                        bufferData.Dispose();
                        throw;
                    }
                }
            }

            return bufferData;
        }

        /// <summary>
        /// 将当前对象写到指定的数据流中
        /// </summary>
        /// <param name="bufferData">目标数据</param>
        /// <param name="writer">当前对象要写入数据的BinaryWriter</param>
        public virtual void ToBufferData(DataSource bufferData, BinaryWriter writer)
        {
            this.WriteData(bufferData, writer);
        }

        /// <summary>
        /// 将当前对象写到指定的数据流中
        /// </summary>
        /// <param name="bufferData">目标数据</param>
        public void ToBufferData(ArraySpan<byte> bufferData)
        {
            long length = this.GetWriteDataLength();
            if (length > bufferData.Capcity - bufferData.Offset)
            {
                throw new ArgumentException($"目标数据容量不足,需要\"{length}\",只有\"{bufferData.Capcity - bufferData.Offset}\"");
            }

            if (length <= 0)
            {
                length = bufferData.Capcity - bufferData.Offset;
            }

            using (var ms = new MemoryStream(bufferData.Array, bufferData.Offset, (int)length))
            {
                this.WriteData(null, new BinaryWriter(ms));
                bufferData.Check((int)ms.Position);
            }
        }


        /// <summary>
        /// 将当前对象写到指定的数据流中
        /// </summary>
        /// <param name="bufferData">目标数据</param>
        /// <param name="writer">当前对象要写入数据的BinaryWriter</param>
        public void ToBufferData(ArraySpan<byte> bufferData, BinaryWriter writer)
        {
            long length = this.GetWriteDataLength();
            if (length > bufferData.Capcity - bufferData.Offset)
            {
                throw new ArgumentException($"目标数据容量不足,需要\"{length}\",只有\"{bufferData.Capcity - bufferData.Offset}\"");
            }

            var beginPos = writer.BaseStream.Position;
            this.WriteData(null, writer);
            bufferData.Update((int)beginPos, (int)(writer.BaseStream.Position - beginPos));
        }

        /// <summary>
        /// 将当前对象写到指定的数据流中
        /// </summary>
        /// <param name="buffer">目标数据</param>
        /// <param name="statIndex">目标数据起始位置</param>
        /// <returns>写入数据长度</returns>
        public int ToBytes(byte[] buffer, int statIndex)
        {
            using (var ms = new MemoryStream(buffer, statIndex, buffer.Length - statIndex))
            {
                this.WriteData(null, new BinaryWriter(ms));
                ms.Flush();
                return (int)(ms.Position - statIndex);
            }
        }

        /// <summary>
        /// 将当前对象写到指定的数据流中
        /// </summary>
        /// <returns>目标数据</returns>
        public byte[] ToBytes()
        {
            long length = this.GetWriteDataLength();
            if (length < 0)
            {
                using (var ms = new MemoryStream())
                {
                    this.WriteData(null, new BinaryWriter(ms));
                    ms.Flush();
                    return ms.ToArray();
                }
            }
            else if (length > int.MaxValue)
            {
                throw new NotSupportedException($"获取到的数据长度为\"{length}\",此方法不支持.");
            }

            else
            {
                byte[] buffer = new byte[length];
                using (var ms = new MemoryStream(buffer))
                {
                    this.WriteData(null, new BinaryWriter(ms));
                }
                return buffer;
            }
        }


        /// <summary>
        /// 获取当前对象转换成字节数据长度
        /// </summary>
        /// <returns>当前对象转换成字节数据长度</returns>
        public long GetBufferLength()
        {
            return this.GetWriteDataLength();
        }

        /// <summary>
        /// 获取要写的数据总长度
        /// </summary>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        protected virtual long GetWriteDataLength()
        {
            return -1L;
        }

        /// <summary>
        /// 写数据
        /// </summary>
        /// <param name="bufferData">目标数据</param>
        /// <param name="writer"></param>
        /// <exception cref="NotImplementedException"></exception>
        protected virtual void WriteData(DataSource bufferData, BinaryWriter writer)
        {
            throw new NotImplementedException();
        }








        /// <summary>
        /// 将数据buffer解析到当前对象
        /// </summary>
        /// <param name="buffer">数据</param>
        /// <param name="startIndex">数据起始偏移位置,小于等于0从头开始</param>
        /// <param name="length">数据长度，小于0到结尾</param>
        public void FromBufferData(byte[] buffer, int startIndex, int length)
        {
            this.Reset();
            DataSource sourceData = new DataSource(buffer, startIndex, length);
            this.PrimitiveFromBufferData(sourceData);
        }

        /// <summary>
        /// 将数据buffer解析到当前对象
        /// </summary>
        /// <param name="buffer">传输数据</param>
        public void FromBufferData(byte[] buffer)
        {
            this.Reset();
            DataSource sourceData = new DataSource(buffer);
            this.PrimitiveFromBufferData(sourceData);
        }

        /// <summary>
        /// 将数据解析到当前对象
        /// </summary>
        /// <param name="bufferData">数据</param>
        public void FromBufferData(DataSource bufferData)
        {
            if (bufferData == null)
            {
                throw new ArgumentNullException(nameof(bufferData));
            }

            if (bufferData.Length <= 0)
            {
                return;
            }

            this.Reset();
            this.PrimitiveFromBufferData(bufferData);
        }

        /// <summary>
        /// 将数据buffer解析到当前对象
        /// </summary>
        /// <param name="bufferData">数据</param>
        /// <param name="reader">数据读取器</param>
        public void FromBufferData(DataSource bufferData, BinaryReader reader)
        {
            this.Reset();
            this.ReadData(bufferData, reader);
        }

        /// <summary>
        /// 将数据解析到当前对象
        /// </summary>
        /// <param name="bufferData">数据</param>
        unsafe protected virtual void PrimitiveFromBufferData(DataSource bufferData)
        {
            switch (bufferData.DataType)
            {
#if !NET4_0
                case DataSourceType.Memory:
                    byte[] memoryBuffer = bufferData.MemoryBuffer.ToArray();
                    using (var ms = new MemoryStream(memoryBuffer))
                    {
                        this.ReadData(bufferData, new BinaryReader(ms));
                    }
                    break;
#endif
                case DataSourceType.Bytes:
                    using (var ms = new MemoryStream(bufferData.Bytes, bufferData.Offset, bufferData.Length))
                    {
                        this.ReadData(bufferData, new BinaryReader(ms));
                    }
                    break;
                case DataSourceType.File:
                    using (var fs = new FileStream(bufferData.FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                    {
                        fs.Position = bufferData.LongOffset;
                        this.ReadData(bufferData, new BinaryReader(fs));
                    }
                    break;
                case DataSourceType.Stream:
                    bufferData.Stream.Position = bufferData.LongOffset;
                    this.ReadData(bufferData, new BinaryReader(bufferData.Stream));
                    break;

                case DataSourceType.BinaryReader:
                    bufferData.Reader.BaseStream.Position = bufferData.LongOffset;
                    this.ReadData(bufferData, bufferData.Reader);
                    break;
                case DataSourceType.DataPtr:
                    using (var ums = new UnmanagedMemoryStream(bufferData.DataPtr, bufferData.LongLength))
                    {
                        this.ReadData(bufferData, new BinaryReader(ums));
                    }
                    break;
                case DataSourceType.Extend:
                default:
                    throw new NotSupportedException($"不支持的解析数据类型\"{bufferData.DataType.ToString()}\",可通过重写本方法实现");
            }
        }

        /// <summary>
        /// 读取数据
        /// </summary>
        /// <param name="sourceData">原始数据</param>
        /// <param name="reader">数据读取器</param>
        /// <exception cref="NotImplementedException"></exception>
        protected virtual void ReadData(DataSource sourceData, BinaryReader reader)
        {
            throw new NotImplementedException();
        }




        /// <summary>
        /// 重置当前数据对象
        /// </summary>
        public virtual void Reset()
        {

        }



        /// <summary>
        /// 指示当前对象释放标识[true:已释放;false:未释放]
        /// </summary>
        protected bool _disposabled = false;
        /// <summary>
        /// 获取当前对象释放标识[true:已释放;false:未释放]
        /// </summary>
        [Browsable(false)]
        public bool Disposed
        {
            get { return this._disposabled; }
        }

        /// <summary>
        /// 释放非托管资源
        /// </summary>
        public void Dispose()
        {
            if (this._disposabled)
            {
                return;
            }
            this._disposabled = true;

            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 释放非托管资源
        /// </summary>
        /// <param name="disposing">释放标识</param>
        protected virtual void Dispose(bool disposing)
        {

        }
    }
}
